#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiManager.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <WebSocketsServer.h>
#include <WiFiUdp.h>
#include <EEPROM.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
#include "OneButton.h"

#include "Image.h"
#include "Pixfont.h"
#include "Cnfont.h"

#define WIFI_NAME "XMatrix"
#define MDNS_NAME "XMatrix"
#define xres 32            // Total number of  columns in the display
#define yres 8            // Total number of  rows in the display

#define TIME_MODE 1
#define DAY_MODE 2
#define DATE_LOOP_MODE 3
#define WEATHER_MODE 4
#define RAINING_MODE 5
#define BFANS_MODE 6
#define FLASH_BACK_MODE 7
#define DRAW_MODE 8
#define MODE_MAX 7
int LOOP_MODE_NOW = 0;
unsigned long loopTime = 0;
unsigned long modeLoopTime = 0;
unsigned long colonTime = 0;
unsigned long lastTime = 0;

/*******************灯板参数定义*********************************/
#define LED_PIN     4  //灯板输入IO口选择
#define BRIGHTNESS  250  //默认背光亮度
#define LED_TYPE    WS2812  //灯珠类型
#define COLOR_ORDER GRB  //色彩顺序

/*******************EEPROM定义*********************************/
#define EEPROM_SIZE 256
#define EEPROM_GAIN         0 //确定是否已存有数据
#define EEPROM_MODE         1 //模式
#define EEPROM_COLOR        2 //时钟总颜色（冒号）
#define EEPROM_COLOR_H      3 //时钟小时颜色
#define EEPROM_COLOR_M      4 //时钟分钟颜色
#define EEPROM_COLOR_S      5 //时钟秒钟颜色
#define EEPROM_COLOR_W      6 //星期颜色
#define EEPROM_BRIGHTNESS   7 //亮度
#define EEPROM_PIC_X        8 //图片X轴
#define EEPROM_PIC_Y        9 //图片Y轴
#define EEPROM_CLOCK_MODE   10 //时钟风格
#define EEPROM_DATE_MODE    11 //日期风格
#define EEPROM_LOOP_MODE    12 //切换风格
#define EEPROM_B_FANS_MODE  13 //粉丝数风格
#define EEPROM_COLOR_B      14 //粉丝数颜色

#define EEPROM_CITY_CODE    20 //城市code
#define EEPROM_KEY          80

#define EEPROM_CITY_CODE_LEN    8 //城市code长度
#define EEPROM_KEY_LEN    16 //城市code长度

#define EEPROM_CONFIRM 10 //判断配置是否已保存，每次变动之后需要更改
/*************按钮定义**************/
#define buttonPin 2
OneButton button(buttonPin, true, false);

int brightnessNow = 250;
int Mode = 1;//模式
int hue = 180;//总颜色
int hueh = 180;
int huem = 180;
int hues = 180;
int huew = 180;
int hueb = 180;
int pic_x = 8;
int pic_y = 7;
int clockMode = 1;
int dateMode = 1;
int loopMode = 1;
int bFansMode = 2;
int weatherMode = 1;
int GlobalX = 0;              // 全局X，摄影机
int GlobalY = 0;              // 全局Y，摄影机
char XMatrix[] = "XMATRIX";

uint8_t saturation = 100;//饱和度
uint8_t value = 100;//明度

bool colon = true;//冒号

// 代码雨配置 8列为一组 共四组
int columnIndex[8];
int stepIndex[8];
int glow[8];
bool Running[8]={false, false, false, false, false, false, false, false};
int stepsTillNext[8] = {0, 6, 2, 11, 4, 10, 7, 1};
int colorh = 120;//默认绿色

/*******B站相关*******/
long Fansnumber = -1;    //粉丝数
HTTPClient http;    //创建HTTPClient 实例
String UID = "4772302";    //bilibili UID
String FansURL = "https://api.bilibili.com/x/relation/stat?vmid=" + UID;
/*******************天气参数定义*********************************/
//YY天气官网地址：http://www.yytianqi.com/api.html
String CITY_CODE = "CH210101";
String YY_KEY = "v7ubouwmejlolqmb";

/******flash back 相关****/
int itvl = 0;

/*******************灯板参数定义*********************************/
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(xres, yres, 1, 1, LED_PIN,
                            NEO_MATRIX_TOP  + NEO_TILE_LEFT  + NEO_MATRIX_COLUMNS   + NEO_MATRIX_PROGRESSIVE +
                            NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
                            NEO_GRB + NEO_KHZ800);

//RGB颜色数组https://www.sojson.com/rgb.html

int  H;
int  M;
int  S;//秒
int falsh_time = 0;
int falsh_interval = 1500;
int D;//星期几 0为周日
int monthDay = 1;
int currentMonth = 1;
String temp_read = "00";
String hum_read = "00";
String tq_code = "00";
char tq_str[] = "获取中";
int tq_len = 9;
//***************************定时器配置**************************
int count = 0;
//***************************文字显示配置************************
char* message_str;      
int cnstr_p, cnstr_l = 0;
int wordshow_i = 0;
//********滚动显示参数**********//
int cnstr_pos[200]; //cnstr_pos：Cnfont8x8 字体字符串中各字符偏移像素
bool loopChangeStatus = false; //是否处于模式切换过程中

String bitdata20[10]={
  "111101101101111","010110010010111",
  "111001111100111","111001111001111",
  "101101111001001","111100111001111",
  "111100111101111","111001001001001",
  "111101111101111","111101111001111"
};
// setup server ports
ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);

WiFiUDP udp;
NTPClient timeClient(udp, "ntp1.aliyun.com", 60 * 60 * 8, 30 * 60 * 1000);

//***************************网页配置****************************
char socketMessage[500] = { 'p', '1', '1' };
char settingMessage[500] = {0};
//**********************字体****************************
enum charfont
{
    Apple4x6,   //全
    Apple5x7,   //全
    Apple6x10,  //全 字体偏大
    Pix5x7,     //1~9 A~Z a~z
    Atari8x8,   //A~Z 0~9
    Undertale8x8, //全
    Blodnum8x8, //0~9
    Cnfont8x8,  //中英文
    Clock3x5,   //0~9 :
    Clock3x4,   //0~9
};

struct FontAttribute  //字体属性
{
    const unsigned char *font_bitmap;  //字体数组
    const unsigned char *font_index;  //字体编码数组
    int font_num;   //字体包含字符数
    int font_width; //字符宽度
    int font_height; //字符高度
    int font_test_x; //测试用：字符居中偏移
    int font_test_y; //测试用：字符居中偏移
    int font_test_space;  //相邻字符偏移像素
};

struct FontAttribute fontattribute[]=    //各字体属性设置
{
    {
        apple4x6_bitmap,
        apple_index,
        sizeof(apple_index) / sizeof(apple_index[0]),
        4,
        6,
        3,
        1,
        4,
    },
    {
        apple5x7_bitmap,
        apple_index,
        sizeof(apple_index) / sizeof(apple_index[0]),
        5,
        7,
        2,
        1,
        5,
    },
    {
        apple6x10_bitmap,
        apple6x10_index,
        sizeof(apple6x10_index) / sizeof(apple6x10_index[0]),
        6,
        10,
        2,
        -1,
        6,
    },
    {
        Pix5x7_bitmap,
        Pix5x7_index,
        sizeof(Pix5x7_index) / sizeof(Pix5x7_index[0]),
        5,
        7,
        2,
        1,
        6,
    },
    {
        Atari8x8_bitmap,
        Atari8x8_index,
        sizeof(Atari8x8_index) / sizeof(Atari8x8_index[0]),
        8,
        8,
        1,
        1,
        8,
    },
    {
        Undertale8x8_bitmap,
        Undertale8x8_index,
        sizeof(Undertale8x8_index) / sizeof(Undertale8x8_index[0]),
        8,
        8,
        1,
        1,
        8,
    },
    {
        Blodnum8x8_bitmap,
        Blodnum8x8_index,
        sizeof(Blodnum8x8_index) / sizeof(Blodnum8x8_index[0]),
        8,
        8,
        0,
        0,
        8,
    },
    {
        Cnfont_bitmap,
        (const unsigned char *)Cnfont_index,
        CNFONTLEN,
        8,
        8,
        0,
        0,
        8,
    },
    {
        Clock3x5_bitmap,
        Clock3x5_index,
        sizeof(Clock3x5_index) / sizeof(Clock3x5_index[0]),
        3,
        5,
        2,
        1,
        4,
    },
    {
        Clock3x4_bitmap,
        Clock3x4_index,
        sizeof(Clock3x4_index) / sizeof(Clock3x4_index[0]),
        3,
        4,
        2,
        2,
        4,
    },
};

//**********************函数声明************************
void ShowIP();
void InitServer();
void WebServer_run();
uint16_t hsv2rgb(uint16_t hue, uint8_t saturation = 100, uint8_t value = 100);
void drawFastXLine(int16_t x, int16_t y, int16_t h, uint16_t c);
void drawFastYLine(int16_t x, int16_t y, int16_t h, int16_t c);
void configSave();
void DrawPic(const unsigned char pic[], bool BgCover = true, int pic_x = 0, int pic_y = 0, int pic_w = xres, int pic_h = yres);
void SetEmoji(int emoji_x, int emoji_y);
void switchMode(bool change, int mode);
void showWiFiConnectDone(int status, bool clear);
void showFirmwareInfo(int x, int y, bool clear);
void showFirmwareInfoEnd();
void showCharacter(int char_x, int char_y, int c, int charfont = Apple4x6, uint16_t showHue = matrix.Color(255,255,255));
void showStringSlip(int char_x, int char_y, int charfont, char *str, int len, uint16_t showColor);
void showStringAlways(int char_x, int char_y, int charfont, char *str, int len, uint16_t showColor);
void showbitnumber(int number, int xlength, int ylength, int x, int y, uint16_t colorxy);
void showbitmap(String bitrgbstr, int xlength, int ylength, int x, int y, uint16_t colorxy);
void showColon(int x,int y,bool l,uint16_t colorxy);
void ClockMode1();
void ClockMode2();
void ClockMode3();
void ClockMode4();
void showWeek(int x, int y, int colorhue, int len);
void configSync(uint8_t num);
void showWeatherDay();
void ParseFans(String url);
void weatherNow(String url);
void showWeatherDay(bool implement);
void showFlashBack(bool implement);
void toTargetMode(int m);
void nextMode();
void Write_String(int a, int b, String str);
String Read_String(int a, int b);
void handleSettingMessage();
